package 遍历器;

import org.antlr.v4.runtime.TokenStream;
import 作用域相关.*;
import 抽象语法树节点.*;
import 抽象语法树节点.字面量类.字符型字面量;
import 抽象语法树节点.字面量类.整数型字面量;
import 抽象语法树节点.字面量类.浮点型字面量;
import 抽象语法树节点.字面量类.逻辑型字面量;
import 抽象语法树节点.语句类.返回语句;

public class 定义监听器 extends 抽象语法树基础监听器 {

    public 定义监听器(TokenStream 符号流) {
        this.符号流 = 符号流;
    }

    public final 日志 日志 = new 日志();

    public 符号表 符号表;
    private final TokenStream 符号流;

    private 作用域 当前作用域;


    @Override
    public void 进入编译单元(编译单元 节点) {
        符号表 = new 符号表(符号流);
        当前作用域 = 符号表.全局作用域;
        节点.作用域 = 当前作用域;
        日志.信息("进入全局作用域");
    }

    @Override
    public void 离开编译单元(编译单元 节点) {
        日志.信息("离开全局作用域");
        当前作用域 = null;
    }


    @Override
    public void 进入类定义(类定义 节点) {
        日志.信息("行 " + 节点.idToken.getLine() + " 进入类定义: " + 节点.名称);
        var 类符号 = new 类符号(节点.名称, 当前作用域, null); // "父类"在访问到父类节点的时候赋值
        节点.作用域 = 当前作用域;
        节点.符号 = 类符号;
        类符号.节点 = 节点;
        
        当前作用域.定义(类符号);
        当前作用域 = 类符号;
    }

    @Override
    public void 离开类定义(类定义 节点) {
        日志.信息("离开类定义: " + 节点.名称);
        当前作用域 = 当前作用域.取外围作用域();
    }

    @Override
    public void 进入父类(父类 节点) {
        节点.作用域 = 当前作用域;
    }



    @Override
    public void 进入方法定义(方法定义 节点) {
        日志.信息("行 " + 节点.idToken.getLine() + " 定义方法 " + 节点.名称);

        var 方法 = new 方法符号(节点.名称, null, 当前作用域);
        节点.作用域 = 当前作用域;
        节点.符号 = 方法;
        方法.节点 = 节点;

        当前作用域.定义(方法);
        当前作用域 = 方法;

        日志.信息("行 " + 节点.idToken.getLine() + " 设置方法 " + 节点.名称 + " 返回值类型 " + 节点.返回值类型);
        方法.类型 = (类型) 节点.作用域.解析(节点.返回值类型.名称);
    }


    @Override
    public void 离开方法定义(方法定义 节点) {
        日志.信息("离开方法 " + 节点.名称);
        当前作用域 = 当前作用域.取外围作用域();
    }


    @Override
    public void 进入变量定义(变量定义 节点) {
        日志.信息("行 " + 节点.idToken.getLine() + " 定义变量 " + 节点.名称);
        var 变量 = new 变量符号(节点.名称, null);
        节点.作用域 = 当前作用域;
        节点.符号 = 变量;
        变量.节点 = 节点;
        当前作用域.定义(变量);
    }

    @Override
    public void 离开变量定义(变量定义 节点) {
        var 符号 = (变量符号) 节点.符号;
        日志.信息("行 " + 节点.idToken.getLine() + " 设置变量 " + 节点.名称 + " 类型 " + 节点.类型.名称);
        符号.类型 = (类型) 节点.作用域.解析(节点.类型.名称);
        节点.求值类型 = 符号.类型;
    }


    @Override
    public void 进入指针定义(指针定义 节点) {
        日志.信息("行 " + 节点.idToken.getLine() + " 定义指针 " + 节点.名称);
        var 指针 = new 变量符号(节点.名称, null);
        节点.作用域 = 当前作用域;
        节点.符号 = 指针;
        指针.节点 = 节点;
        
        当前作用域.定义(指针);
    }


    @Override
    public void 离开指针定义(指针定义 节点) {
        var 符号 = (变量符号) 节点.符号;
        var 类型 = new 指针类型((类型) 节点.作用域.解析(节点.类型.名称));
        日志.信息("行 " + 节点.idToken.getLine() + " 设置指针 " + 节点.名称 + " 类型 " + 类型);
        符号.类型 = 类型;
        节点.求值类型 = 类型;
    }

    @Override
    public void 进入返回语句(返回语句 节点) {
        节点.作用域 = 当前作用域;
    }

    @Override
    public void 进入代码块(代码块 节点) {
        日志.信息("进入代码块");
        var 局部作用域 = new 局部作用域(当前作用域);
        节点.作用域 = 局部作用域;
        当前作用域 = 局部作用域;
    }

    @Override
    public void 离开代码块(代码块 节点) {
        日志.信息("离开代码块");
        当前作用域 = 当前作用域.取外围作用域();
    }

    @Override
    public void 进入类型引用(类型引用 节点) {
        节点.作用域 = 当前作用域;
    }

    @Override
    public void 进入标识符(标识符 节点) {
        节点.作用域 = 当前作用域;
    }

    @Override
    public void 进入整数型字面量(整数型字面量 节点) {
        节点.作用域 = 当前作用域;
    }

    @Override
    public void 进入浮点型字面量(浮点型字面量 节点) {
        节点.作用域 = 当前作用域;
    }

    @Override
    public void 进入字符型字面量(字符型字面量 节点) {
        节点.作用域 = 当前作用域;
    }

    @Override
    public void 进入逻辑型字面量(逻辑型字面量 节点) {
        节点.作用域 = 当前作用域;
    }
}
